/blog/Working on integrating Svelte as a progressive component system
Working on integrating Svelte as a progressive component system.
NOTE: While this whole site is rambling, this blog post is particularly so. This is not a "how-to". More of me just publishing my notes as I go along after finishing the first pass of the Svelte tutorial and trying to create some progressive components for my site (see the default component at the top of the base content page).
So starting a new Svelte app from scratch from inside my /static/
directory with:
npx degit sveltejs/template svelte
We get a templated project directory like so
.
└── svelte
├── package.json
├── public
│ ├── favicon.png
│ ├── global.css
│ └── index.html
├── README.md
├── rollup.config.js
├── scripts
│ └── setupTypeScript.js
└── src
├── App.svelte
└── main.js
So the other starter instructions are to install with npm and start the dev server, which for brevity I’ll follow so:
cd svelte
npm install
npm run dev
This will get the build and rolling build update going, but I don’t really care about the dev server. I’ll get back to running the continuous build without it later. For now this gives us a /build
directory and build artifacts, we just need the javascript bundle.js
and the stylesheet bundle.css
. A quick symlink of those into my standard /static/css
and /static/javascript
directories and now I can access them in the content using my CMS .content_meta file.
...
"template_override": "",
"javascript_include": [
"/static/javascript/bundle.js"
],
"javascript_inline": "",
"css_include": [
"/static/css/bundle.css"
],
"css_inline": "",
...
There are more fields in the meta (which each piece of content has), but those five let you include other javascript and css files, inline snippets or even change the backend rendering template on a per piece of content basis. This is probably meant to be more global and long lived, but this is fine for now.
Save the content meta file, refresh my local page and whoa! Below my footer is the Svelte default starter template thingy.
Rollup looks pretty nice, at least the output is clean and colorful, and I’m looking for a potential replacement for Webpack after hitting a few snags with it so maybe Rollup will be the future replacement? I don’t know, we’ll see.
So to be minimally functional I need to be able to inject the Svelte components where I want them to go in the DOM. Sure I could write some Javascript to place them, but I’m thinking there might be support for that in Svelte itself. I don’t know, I just finished the tutorial so I really have no idea. Let’s look at the API reference.
Custom Element API this looks like what I’m trying to do. I tried creating a custom element in content, which for my CMS can just be to copy the markdown filename and give it the .html extension and replace the file contents with <svelte-demo></svelte-demo>
. The CMS will attempt to render the HTML content just above the markdown file I’m using.
But the element is still at the bottom of the page. Ah, but it now uses the <svelte-demo>
tag. This seems like the right direction, but not quite where I wanted it.
Ok, what else might there be in the API? Not much, but it seems like this should have worked. I must be missing something.
Insert random awesome blog that shows exactly what I missed
So the custom component tag in the content can’t be <svelte-demo></svelte-demo>
it just needs to be <svelte-demo />
. This injects the component in the custom HTML snippet I placed above this content, but it also wipes out the following content I created in Markdown. I’ve seen this before, so without diving into it much I just wrapped the tag in a container div and everything works as expected. This is what my content.html file looks like now:
<div id="svelte-container">
<svelte-demo />
</div>
Well almost, it looks like I have 2 Svelte components now, one in the content card
where I am embedding content where I’m expecting it that is missing the propery something
and one at the bottom of the body
that has the expected default prop(erty). Duplicate Svelte components would be a nasty bug, but I don’t think this is a bug in Svelte, more just something I’m not setting up right yet.
There is a console error about a prop not getting set, so I check that first. A quick change to the html content to set the property something
gets me what I expect in the content embedded component. That’s great as I can pass the prop from the static renderer to the component through the Tera template.
<div id="svelte-container">
<svelte-demo something="Gatewaynode" />
</div>
But I still have 2 components being rendered into the page. Let’s take a look at the source layout.
.
└── svelte
...
└── src
├── App.svelte
└── main.js
The App.svelte
file is our main place to write Svelte style JS, the main.js
file is vanilla Javascript to initialize and construct the app. So looking in the main.js
file I found the culprit for double rendering.
import App from ’./App.svelte’;
const demo = new App({
// target: document.body,
props: {
something: ’Default’
}
});
export default demo;
The section, commented out here, tells the app to initialize in the document body. Which I don’t need as I’m already declaring it in the DOM where it should be, which is enough for Svelte. So the component renders correctly, you can tell because it has a shadow DOM which only exists for Svelte components, whole apps don’t use a shadow DOM. There is a pretty good explanation of why they have to use a shadow DOM for components here. If you are not familiar with how to see the shadow DOM in the developer tools you can also look at the page source (right click and choose "view page source") and search for "Mars" or "svelte-container" and notice none of the text you see is in the DOM, that’s because it’s being rendered in Javascript in your browser.
Added a small CSS snippet in the .content_meta to add a little border to the HTML above the markdown content that contains the component:
...
"javascript_include": [
"/static/javascript/bundle.js"
],
"javascript_inline": "",
"css_include": [
"/static/css/bundle.css"
],
"css_inline": "#svelte-container{border: 3px solid grey;}",
...
And I think that’s a good place to stop right with what is just a toy implementation of Svelte right now. It’s progress, but it also has a few bugs (component inline style API doesn’t seem to be working, something strange is going on with JS execution inside the component). Time to go back through the tutorial again and takes notes as to what to study as I go.